package com.ehsunbehravesh.mypasswords.exportimport;

import com.ehsunbehravesh.mypasswords.Domain;
import com.ehsunbehravesh.mypasswords.Logger;
import com.ehsunbehravesh.mypasswords.Utils;
import com.ehsunbehravesh.mypasswords.entity.PasswordEntry;
import com.ehsunbehravesh.mypasswords.entity.PasswordTag;
import java.io.File;
import java.util.Date;
import java.util.Observable;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class XMLExporter extends Observable implements Runnable {

  private PasswordEntry[] passwords;
  private PasswordTag[] tags;
  private File file;
  private String key;
  private int minProgress;
  private int maxProgress;
  private int progress;
  private boolean encryptAllFields;

  //<editor-fold defaultstate="collapsed" desc="Constructors & Destructors">
  public XMLExporter(File file, String key, boolean encryptAllFields) {
    this.file = file;
    this.key = key;
    this.minProgress = 0;
    this.maxProgress = 0;
    this.progress = 0;
    this.encryptAllFields = encryptAllFields;

    passwords = PasswordEntry.getAllPasswordEntries();
    tags = PasswordTag.getAllTags();
    maxProgress = passwords.length + tags.length;
  }

  protected void finilize() throws Throwable {
  }
  //</editor-fold>

  //<editor-fold defaultstate="collapsed" desc="Methods">
  @Override
  public void run() {
    Domain domain = Domain.getInstance();
    try {
      DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
      DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
      Document document = documentBuilder.newDocument();

      Element elmRoot = document.createElement("MyPasswords");
      Element elmSpecifications = document.createElement("Specifications");

      Element elmDate = document.createElement("Date");
      elmDate.setTextContent(new Date().getTime() + "");

      Element elmPasswordsCount = document.createElement("PasswordsCount");
      elmPasswordsCount.setTextContent(passwords.length + "");

      Element elmTagsCount = document.createElement("TagsCount");
      elmTagsCount.setTextContent(tags.length + "");

      Element elmVersion = document.createElement("MyPasswordsVersion");
      elmVersion.setTextContent(domain.getMyPasswordsVersion() + "");

      elmSpecifications.appendChild(elmVersion);
      elmSpecifications.appendChild(elmPasswordsCount);
      elmSpecifications.appendChild(elmTagsCount);
      elmSpecifications.appendChild(elmDate);

      Element elmPasswordTags = document.createElement("Tags");

      for (int i = 0; i < tags.length; i++) {
        PasswordTag passwordTag = tags[i];
        Element elmTag = document.createElement("Tag");
        if (encryptAllFields) {
          elmTag.setTextContent(encrypt(passwordTag.getTitle()));
        } else {
          elmTag.setTextContent(passwordTag.getTitle());
        }
        elmPasswordTags.appendChild(elmTag);
        this.progress++;
        setChanged();
        notifyObservers();
      }

      Element elmPasswordEntries = document.createElement("Passwords");

      for (int i = 0; i < passwords.length; i++) {
        Element elmPasswordEntry = document.createElement("Password");
        PasswordEntry passwordEntry = passwords[i];

        Element elmTitle = document.createElement("Title");
        Element elmDescription = document.createElement("Description");
        Element elmUsername = document.createElement("Username");
        Element elmPassword = document.createElement("Password");
        Element elmURL = document.createElement("URL");
        Element elmTags = document.createElement("Tags");

        if (encryptAllFields) {
          elmTitle.setTextContent(encrypt(passwordEntry.getTitle()));
          elmDescription.setTextContent(encrypt(passwordEntry.getDescription()));
          elmUsername.setTextContent(encrypt(passwordEntry.getUsername()));
          elmURL.setTextContent(encrypt(passwordEntry.getUrl()));
        } else {
          elmTitle.setTextContent(passwordEntry.getTitle());
          elmDescription.setTextContent(passwordEntry.getDescription());
          elmUsername.setTextContent(passwordEntry.getUsername());
          elmURL.setTextContent(passwordEntry.getUrl());
        }

        elmPassword.setTextContent(encrypt(passwordEntry.getPassword()));

        PasswordTag[] tags = passwordEntry.readTags();
        for (int j = 0; j < tags.length; j++) {
          PasswordTag passwordTag = tags[j];
          Element elmTag = document.createElement("Tag");
          if (encryptAllFields) {
            elmTag.setTextContent(encrypt(passwordTag.getTitle()));
          } else {
            elmTag.setTextContent(passwordTag.getTitle());
          }
          elmTags.appendChild(elmTag);
        }

        elmPasswordEntry.appendChild(elmTitle);
        elmPasswordEntry.appendChild(elmDescription);
        elmPasswordEntry.appendChild(elmUsername);
        elmPasswordEntry.appendChild(elmPassword);
        elmPasswordEntry.appendChild(elmURL);
        elmPasswordEntry.appendChild(elmTags);

        elmPasswordEntries.appendChild(elmPasswordEntry);

        this.progress++;
        setChanged();
        notifyObservers();
      }

      elmRoot.appendChild(elmSpecifications);
      elmRoot.appendChild(elmPasswordTags);
      elmRoot.appendChild(elmPasswordEntries);
      document.appendChild(elmRoot);

      Source source = new DOMSource(document);
      Result result = new StreamResult(file);

      Transformer transformer = TransformerFactory.newInstance().newTransformer();
      transformer.transform(source, result);
    } catch (TransformerException ex) {
      Logger.log(ex.getMessage());
    } catch (ParserConfigurationException ex) {
      Logger.log(ex.getMessage());
    }
  }

  private String encrypt(String text) {
    try {
      return Utils.encrypt(this.key, text);
    } catch (Exception ex) {
      Logger.log(ex.getMessage());
      return "";
    }
  }
  //</editor-fold>

  //<editor-fold defaultstate="collapsed" desc="Getters & Setters">
  public File getFile() {
    return file;
  }

  public int getMaxProgress() {
    return maxProgress;
  }

  public int getMinProgress() {
    return minProgress;
  }

  public PasswordEntry[] getPasswords() {
    return passwords;
  }

  public int getProgress() {
    return progress;
  }

  public int getProgressPercent() {
    return (int) Math.floor(progress * 100 / maxProgress);
  }

  public PasswordTag[] getTags() {
    return tags;
  }
  //</editor-fold>
}

